K8s 流量调度与 Ready

Query

Kubernetes 的流量调度是以 Pod 为单位还是以容器为单位?影响 Service 中记录是否可接受流量的,是 容器的 ready 状态还是 Pod 的?

在 Kubernetes 中,这两个问题的答案非常明确:

  1. 流量调度是以 Pod 为单位,而不是容器。
  2. 影响 Service 是否转发流量的,是 Pod 的就绪状态(由容器的就绪探针综合决定)。

下面为你详细解释原因:

1. 流量调度以 Pod 为单位

  • Pod 是调度的原子单位:Kubernetes 中最小的部署和调度单元是 Pod。一个 Pod 内可以有一个或多个容器,但它们共享网络命名空间(IP 地址、端口等)、存储卷(Volume)和 IPC 命名空间。
  • Service 的后端是 Pod:当创建 Service(例如 ClusterIP、NodePort 类型)并通过标签选择器(Label Selector)关联后端时,Service 的 Endpoint(或 EndpointSlice)列表中记录的是 Pod 的 IP 地址和端口,而不是容器的 IP(容器没有独立的节点网络 IP)。
  • 负载均衡分发粒度:kube-proxy 或 Ingress Controller 在转发流量时,会将请求分发到不同的 Pod IP 上。同一个 Pod 内的多个容器访问同一个 Service 时,流量也会被均衡到不同的后端 Pod,而不会在容器级别做区分。

举例:假设有一个 Pod 里运行了 nginxsidecar 两个容器,它们共享同一个 Pod IP 10.244.1.5。Service 的 Endpoint 只会记录 10.244.1.5:80(nginx 的端口)。发往该 Service 的流量全部到达这个 Pod IP,然后由 Pod 内部的网络栈根据端口决定交给哪个容器(这里 80 端口属于 nginx)。

2. 影响 Service 流量的关键是 Pod 的就绪状态

Service 是否将某个 Pod 的 IP 加入其负载均衡列表(Endpoint),取决于 Pod 的 Ready 条件。而这个条件是由 Pod 内所有容器的就绪探针(Readiness Probe) 共同决定的。

  • Pod 的就绪状态规则:一个 Pod 被认为是“就绪”的当且仅当 Pod 内的所有容器都处于就绪状态(即通过各自的就绪探针检查,或未定义探针时默认就绪)。
  • 与容器 Ready 的关系:如果 Pod 内任何一个容器的就绪探针失败(或容器未运行),那么整个 Pod 就会被标记为 NotReady。Service 控制器会立刻将该 Pod 的 IP 从 Endpoint 列表中移除,停止向它转发新流量。
  • 与容器 Running 状态的区别:容器仅仅是 Running(进程存在)并不代表它能正常提供服务(比如应用还在加载缓存、预热连接池)。就绪探针正是用来解决这个问题的——只有容器真正“准备好接收流量”时,才允许 Service 把流量放进来。

特殊情况

  • 如果 Pod 里只有一个容器,那么该容器的就绪状态直接决定 Pod 的就绪状态。
  • 如果 Pod 里有多个容器,比如一个主容器和一个 sidecar 辅助容器,那么两个都必须就绪,Pod 才会被 Service 纳入后端。若 sidecar 探针失败,即使主容器完全正常,整个 Pod 也会被踢出 Service 的负载均衡。

总结对比表

问题答案原因
流量调度单位PodService 的 Endpoint 记录的是 Pod IP;kube-proxy 以 Pod 为目标做负载均衡。
决定是否接收流量Pod 的就绪状态Pod 的就绪状态由其所有容器的就绪探针逻辑与(AND)结果决定。只有所有容器都就绪,Pod 才会被加入 Service。

实践建议

  • 如果你希望同一个 Pod 内的不同容器独立接收 Service 流量(例如一个提供 HTTP API,另一个提供 gRPC),你需要创建两个不同的 Service,分别使用不同的端口和标签选择器来指向同一个 Pod(但监听不同容器端口)。注意:此时流量到达 Pod 后,内核会根据目标端口自动路由到正确的容器。
  • 在定义多容器 Pod 时,务必为每个容器都配置合适的就绪探针(尤其是那些需要初始化时间或会暂时不可用的辅助容器),否则 sidecar 容器一旦探针失败,整个 Pod 就会被 Service 剔除,导致主容器也无辜“掉线”。
Query

Pod 的就绪状态与容器 Ready 分别记录在哪个资源的哪个字段?

Pod 的就绪状态和每个容器的 Ready 状态,分别记录在 Pod 资源的不同字段中。要获取这些信息,可以通过 kubectl get pod <pod-name> -o yaml 来查看完整的 YAML 输出。

📝 核心字段详解

状态类型资源字段路径取值含义
Pod 整体就绪状态Pod.status.conditions 数组中 type: Readystatus 字段"True", "False", "Unknown"True:Pod 整体就绪,已列入 Service 负载均衡列表[reference:0]。 False:Pod 未就绪,已从 Service 的 Endpoint 中移除。
单个容器就绪状态Pod.status.containerStatuses[] 数组中对应容器的 .ready 字段true, falsetrue:该容器已通过就绪探针检查[reference:1]。 false:容器未就绪。
Pod 所有容器就绪状态Pod.status.conditions 数组中 type: ContainersReadystatus 字段"True", "False", "Unknown"True:Pod 内所有容器都处于就绪状态[reference:2][reference:3]。

🔗 与流量调度机制的关系

理解这些字段是理解 Kubernetes 流量调度原理的关键:

  1. 核心规则:一个 Pod 只有在其 Ready 条件为 True 时,才会被 Service 作为流量转发的后端。这是上一轮对话的核心结论。
  2. 字段联动:Pod 的 Ready 条件是一个“聚合”结果,其计算逻辑包含一个严格的“与”运算:
    • 必要条件:Pod 必须已调度成功 (PodScheduled)、所有初始化容器已完成 (Initialized)[reference:4]。
    • 决定因素:Pod 的 Ready 条件,最终取决于 .status.conditionsContainersReady 的条件是否为 True[reference:5]。
    • 关键联动:而 ContainersReady 条件是否为 True,又取决于 .status.containerStatuses每一个容器的 ready 字段是否都为 true

💡 示例:如何解读 Pod 状态

假设你有一个运行 nginxsidecar 两个容器的 Pod。

  • 情况 A:容器均正常

    • status.containerStatuses[0].ready (nginx): true
    • status.containerStatuses[1].ready (sidecar): true
    • status.conditionstype: ContainersReadystatus: "True"
    • status.conditionstype: Readystatus: "True"
    • 结果:Pod IP 出现在 Service 的 Endpoint 列表中,正常接收流量。
  • 情况 B:sidecar 探针失败

    • status.containerStatuses[0].ready (nginx): true
    • status.containerStatuses[1].ready (sidecar): false
    • status.conditionstype: ContainersReadystatus: "False" (因为有容器未就绪)
    • status.conditionstype: Readystatus: "False" (因为 ContainersReadyFalse)
    • 结果:Pod IP 会立刻从 Service 的 Endpoint 中移除,即使主容器 nginx 仍在运行。

查看命令:你也可以直接使用 kubectl describe pod <pod-name> 命令,在输出的 ConditionsContainer Statuses 部分清晰地看到这些字段的值,这比直接查看 YAML 更直观。

⚙️ 后台处理机制:从 Pod 状态到 Endpoint 更新

理解 Pod 状态字段后,再看 Kubernetes 后台的自动化流程,能更好地理解为什么 Pod 未就绪时流量就会中断:

  1. 状态监控:集群中的 Endpoint Controller 持续监听 Pod 资源的 .status.conditions 字段的变化。
  2. 更新 Endpoints:一旦发现某个 Pod 的 Ready 条件变为 False,控制器会立即更新与 Service 同名的 Endpoints 资源(或 EndpointSlice 资源)。
  3. 流量切除
    • 传统 Endpoints:会将此 Pod 的 IP 从 subsets.addresses (就绪地址列表) 移动到 subsets.notReadyAddresses (未就绪地址列表)[reference:6][reference:7]。
    • 新版 EndpointSlice:会更新该端点条目,将其 conditions.ready 字段标记为 false[reference:8]。
  4. 数据平面生效:在每个节点上运行的 kube-proxy 组件会 watch Endpoints / EndpointSlice 的变化,并更新本地的 IPVS 或 iptables 规则,从负载均衡池中移除故障 Pod 的 IP。至此,新的流量将不再发往该 Pod。
Query

.status.conditions 数组中 type: Ready 的 status 字段,与 .status.conditions 数组中 type: ContainersReady 的 status 字段,有什么不同吗?什么时候这两个字段会不同?

这两个字段分别代表 Pod 的整体就绪状态容器就绪状态的聚合。虽然它们密切相关,但 并不总是相同,差异源于 Ready 条件会考虑 容器就绪之外的附加因素

核心区别

字段含义判断依据
ContainersReadyPod 内所有普通容器是否都已就绪(通过各自的就绪探针)仅取决于 .status.containerStatuses[].ready 字段:所有容器该字段均为 true 时,ContainersReadyTrue;否则为 False
ReadyPod 整体是否可以接收 Service 流量基于多个条件的“逻辑与”: - PodScheduled = True - Initialized = True - ContainersReady = True - 所有自定义 ReadinessGates 都满足

ReadinessGates 是 Pod 规范 .spec.readinessGates 中定义的一组额外条件,允许用户自定义影响 Pod 就绪状态的判断逻辑(例如等待外部控制器确认网络规则已生效)。

什么时候两者会不同?

两个字段的值可能不一致,主要有两种情况:

1. 自定义 ReadinessGates 未满足(最常见)

这是设计上最主要的差异场景。

  • 现象ContainersReady = True,但 Ready = False
  • 原因:Pod 定义了 .spec.readinessGates,例如要求某个自定义条件 custom-condition 必须为 True,但该条件尚未满足。
  • 影响:Pod 容器已经完全就绪,但整体仍被 Service 排除在负载均衡之外,直到所有门控条件通过。
  • 示例
    spec:
      readinessGates:
      - conditionType: "example.com/network-ready"
    如果该条件在 .status.conditions 中状态不为 True,则 ReadyFalse

2. Pod 正在终止(Terminating)

当 Pod 被删除或节点压力导致驱逐时,kubelet 会将 Pod 标记为不可用,但容器可能仍处于运行且就绪状态。

  • 现象ContainersReady = True,而 Ready = False(或很快变为 False)。
  • 原因:Pod 的 deletionTimestamp 被设置,kubelet 开始优雅终止流程,主动将 Ready 条件置为 False 以停止接收新流量,但容器尚未停止,探针可能仍然返回成功。
  • 影响:Service 立即摘除该 Pod,但正在处理的现有连接可继续完成(取决于终止宽限期)。

3. 其他调度或初始化问题(理论上差异短暂)

虽然不太常见,但以下情况也可能造成短暂差异:

  • PodScheduled = True 但节点网络尚未就绪(由外部控制器反映在 ReadinessGates 中)。
  • Initialized = True 但容器已就绪,而某个自定义条件(如挂载外部存储)尚未满足。

注意:如果所有普通容器都已就绪(ContainersReady=True),但 PodScheduledInitializedFalse,实际上 ContainersReady 不可能为 True(因为容器运行的前提是调度完成且初始化容器结束)。所以这两个条件不会导致“ContainersReady=TrueReady=False”,它们只会让两者同时为 False

快速判断方法

使用 kubectl get pod <pod-name> -o yaml 查看 .status.conditions 数组,对比 type: ContainersReadytype: Readystatus 字段:

  • 两者均为 True:正常,Pod 接收流量。
  • 两者均为 False:容器未就绪(探针失败或未运行),或调度/初始化未完成。
  • ContainersReady = TrueReady = False:存在自定义就绪门控未满足,或 Pod 正在终止。这是需要重点排查的差异情况。

实际意义

了解这个区别有助于诊断“容器已就绪但 Service 仍不转发流量”的问题。此时应检查:

  1. Pod 是否定义了 .spec.readinessGates,以及对应条件的状态。
  2. Pod 是否处于 Terminating 状态(.metadata.deletionTimestamp 非空)。